package com.example.demo.services;


import com.example.demo.utils.FileMetadata;
import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.io.*;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class MinioService {

    private final MinioClient minioClient;

    private final static Logger logger = LoggerFactory.getLogger(MinioService.class);

    public MinioService(MinioClient minioClient) {
        this.minioClient = minioClient;
    }

    //    创建存储桶
    public void bucketExists(String bucketName) throws Exception {
        String policyJsonString = "";
        try {
            boolean exist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (!exist) {
//            存储桶不存在则创建
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
//                创建存储桶的时候设置文件访问策略
                minioClient.setBucketPolicy(SetBucketPolicyArgs.builder()
                        .bucket(bucketName).config(policyJsonString).build());
            }
        } catch (Exception e) {
            // 记录异常信息
            System.err.println("处理存储桶信息时出现错误：" + e.getMessage());
            throw new Exception("处理存储桶信息时出现错误：{}", e);
        }
    }

    /**
     * @param bucketName   minio中的存储桶名称
     * @param fileContent  文件内容    字节数据
     * @param fileMetadata 文件的元数据
     * @throws ServerException
     * @throws InsufficientDataException
     * @throws ErrorResponseException
     * @throws IOException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     * @throws InvalidResponseException
     * @throws XmlParserException
     * @throws InternalException
     */
    //    上传文件        在这里抛出的异常可以直接使用Exception，看个人想法
    public void putFileInMinio(String bucketName, byte[] fileContent, FileMetadata fileMetadata) throws
            ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        try {
            //        检查存储桶是否存在
            boolean buckExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (!buckExist) {
//                存储桶不存在则创建反之添加数据
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
            } else {
//                构造聊天消息Id
                String chatId = fileMetadata.getSenderId() + "-" + fileMetadata.getReceiverId();
//                构建文件元数据
                String fileMetadataKey = fileMetadata.getSenderId() + "-" + fileMetadata.getReceiverId();
                String fileKey = fileMetadata.getFileKey();
                String fileName = fileMetadata.getFileName();
                String mimeType = fileMetadata.getMimeType();
                // 使用 ByteArrayInputStream 将 byte[] 转换为 InputStream
                InputStream fileInputStream = new ByteArrayInputStream(fileContent);
//                自定义上传文件的元数据
                Map<String, String> metadata = new HashMap<>();
                metadata.put("x-amz-meta-file-logotype", fileMetadataKey);
                metadata.put("x-amz-meta-key", fileKey);

//                putObject适合分块上传文件，但是要设置分块上传的大小
//                动态设置分块大小
                long partSize;
                partSize = fileContent.length < 5 * 1024 * 1024 ? -1 : 5 * 1024 * 1024;
//                将文件上传到minio bucket
                minioClient.putObject(
                        PutObjectArgs.builder()
                                .bucket(bucketName)
                                .object(fileName)
                                .contentType(mimeType)
                                .stream(fileInputStream, fileContent.length, partSize)
                                .userMetadata(metadata).build()
                );
//                System.out.println("文件上传成功");
            }
//        将文件上传到minio bucket
        } catch (Exception e) {
            throw new IOException("上传大文件到Minio时出错：" + e.getMessage());
        }
    }


    //    由于bucket是开放的所以访问链接和生成链接下载一个即可
    //    获取文件链接的函数（1、在上传文件之后获取这个链接然后存储到redis中，也方便后期的文件查询）
//    1、访问链接
    public String getObjectVisitURL(String bucketName, String fileName, String fileKey) {
        try {
//            获取对象的统计信息
            StatObjectResponse statObjectResponse = minioClient.statObject(StatObjectArgs.builder()
                    .bucket(bucketName).object(fileName).build());
//            获取自定义元数据
            Map<String, String> customMetadata = statObjectResponse.userMetadata();
//            将Map的键值对转换为列表
            List<Map.Entry<String, String>> entryList = new ArrayList<>(customMetadata.entrySet());
//            检查是否至少存在两个键值对
            if (entryList.size() >= 2) {
//                获取第二个键的值
                String fileKeyValue = entryList.get(1).getValue();    // 第二个键对应的值
                if (fileKeyValue.equals(fileKey)) {
//                    生成文件的访问链接
                    String presignedUrl = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                            .bucket(bucketName).object(fileName)
                            .method(Method.GET)
//                             设置有效期为三天
                            .expiry(259200).build());
//                    返回生成的访问和下载链接
//                    System.out.println("获取的文件访问链接：" + presignedUrl);
                    return presignedUrl;
                } else {
                    System.out.println("匹配失败");
                    return null;
                }
            }
        } catch (Exception e) {
            logger.error("获取文件访问链接时出现错误：{}" + e.getMessage());
            return null;
        }
        return null;
    }

    //   创建临时访问链接
    public String getTemporaryUrl(String fileName) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                .bucket("chat-file").object(fileName)
                .method(Method.GET)
                .expiry(259200).build());
    }

    //    通过文件的fileKey获取文件访问链接
    public void getFileUrlInfoMinioByFileKey(String fileKey, String fileName) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
//        获取minio中的文件对象统计信息（包含了自定义元数据信息）
        StatObjectResponse statObjectResponse = minioClient.statObject(StatObjectArgs.builder()
                .bucket("chat-file").object(fileName).build());
        System.out.println("获取到的文件统计信息：");
//        获取自定义元数据信息
        Map<String, String> customMetadata = statObjectResponse.userMetadata();
//        将Map的键值对转换为列表信息
        List<Map.Entry<String, String>> entryList = new ArrayList<>(customMetadata.entrySet());
//        检查是否至少存在两个键值对信息
        if (entryList.size() >= 2) {
            System.out.println(entryList);
            System.out.println(statObjectResponse.userMetadata().getClass().getName());
        } else {
            System.out.println("信息不全，确保文件上传成功！");
        }
    }

    //    上传文件并返回文件的访问链接
    public String uploadFile(String objectName, InputStream inputStream, long size, String contentType, String userId) throws Exception {
        // 确保对象名称包含用户ID前缀
        String finalObjectName = userId + "-" + objectName;

        // 上传文件到MinIO
        minioClient.putObject(
                PutObjectArgs.builder()
                        .bucket("snow-chat-app-user-avatars")
                        .object(finalObjectName)
                        .stream(inputStream, size, -1)  // -1表示分片大小为默认值
                        .contentType(contentType)
                        .build()
        );

        // 生成预签名URL（注意这里使用finalObjectName而不是file.getName()）
        return minioClient.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs.builder()
                        .bucket("snow-chat-app-user-avatars")
                        .object(finalObjectName)
                        .method(Method.GET)
                        .expiry(259200)  // 3天有效期（60*60*24*3）
                        .build()
        );
    }


}
